import json
import os
from typing import Dict, List, Optional, Tuple

from src.db import get_pdf_tags


MANIFEST_FILENAME = ".pdfer-tags.json"


def _safe_relpath(path: str, root: str) -> str:
    try:
        return os.path.relpath(path, root)
    except Exception:
        return os.path.basename(path)


def export_directory_tags(conn, directory: str) -> str:
    """
    将目录下当前收录的 PDF 的标签导出到 <directory>/.pdfer-tags.json。
    包含强/弱标识字段，跨机器拷贝后可在扫描时恢复标签。
    返回导出文件路径。
    """
    # 获取该目录下的所有当前路径 PDF 及其属性
    rows = conn.execute(
        """
        SELECT p.id, p.doc_id, p.page_count, p.first_page_text_sha1, p.description,
               (SELECT path FROM paths WHERE pdf_id = p.id AND is_current = 1 LIMIT 1) AS path
        FROM pdfs p
        WHERE EXISTS (
            SELECT 1 FROM paths pa
            WHERE pa.pdf_id = p.id AND pa.is_current = 1 AND pa.path LIKE ?
        )
        """,
        (directory.rstrip("\\/") + os.sep + '%',),
    ).fetchall()

    entries: List[Dict] = []
    for r in rows:
        pdf_id = int(r[0])
        doc_id = r[1]
        page_count = int(r[2] or 0)
        first_sha1 = r[3]
        description = r[4]
        path = r[5] or ""
        tags = get_pdf_tags(conn, pdf_id)
        entries.append(
            {
                "filename": _safe_relpath(path, directory),
                "tags": tags,
                "description": description,
                "doc_id": doc_id,
                "page_count": page_count,
                "first_page_text_sha1": first_sha1,
            }
        )

    manifest = {
        "version": 1,
        "directory": os.path.abspath(directory),
        "entries": entries,
    }
    out_path = os.path.join(directory, MANIFEST_FILENAME)
    with open(out_path, "w", encoding="utf-8") as f:
        json.dump(manifest, f, ensure_ascii=False, indent=2)
    return out_path


class TagManifest:
    def __init__(self, entries: List[Dict]):
        # 建立多种索引：doc_id、弱标识、文件名
        self.by_doc_id: Dict[str, Tuple[List[str], Optional[str]]] = {}
        self.by_weak: Dict[Tuple[int, Optional[str]], Tuple[List[str], Optional[str]]] = {}
        self.by_filename: Dict[str, Tuple[List[str], Optional[str]]] = {}
        for e in entries:
            tags = list(e.get("tags") or [])
            description = e.get("description")
            doc_id = e.get("doc_id")
            page_count = int(e.get("page_count") or 0)
            first_sha1 = e.get("first_page_text_sha1")
            filename = str(e.get("filename") or "")
            tags_desc_tuple = (tags, description)
            if doc_id:
                self.by_doc_id[str(doc_id)] = tags_desc_tuple
            self.by_weak[(page_count, first_sha1)] = tags_desc_tuple
            if filename:
                # 使用小写路径匹配
                self.by_filename[filename.replace("\\", "/").lower()] = tags_desc_tuple

    def match_tags(self, *, doc_id: Optional[str], page_count: int, first_sha1: Optional[str], filename: str) -> Tuple[List[str], Optional[str]]:
        if doc_id and doc_id in self.by_doc_id:
            return self.by_doc_id[doc_id]
        k = (page_count, first_sha1)
        if k in self.by_weak:
            return self.by_weak[k]
        key = filename.replace("\\", "/").lower()
        return self.by_filename.get(key, ([], None))


def load_manifest_if_exists(root: str) -> Optional[TagManifest]:
    path = os.path.join(root, MANIFEST_FILENAME)
    if not os.path.exists(path):
        return None
    try:
        with open(path, "r", encoding="utf-8") as f:
            data = json.load(f)
        entries = data.get("entries") or []
        return TagManifest(entries)
    except Exception:
        return None


